﻿package  
{
	import flash.display.*;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.text.AntiAliasType;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import flash.net.URLRequest;
	import flash.system.Capabilities;
	
	/**
	 * ...
	 * @author ...
	 */
	public class Slide extends Sprite
	{
		public static var LOADED:String = "SlideLoaded";
		public static var FAILED_LOAD:String = "SlideLoadFailed";
		public static var FADE_IN_COMPLETE:String = "FadeInComplete";
		public static var FADE_OUT_COMPLETE:String = "FadeOutComplete";

		private var m_url:String;
		private var m_description:String;
		private var m_loaded:Boolean;
		private var m_percentLoaded:Number;
		private var m_bmp:Bitmap;
		private var m_caption:TextField;

		private var m_unloadTimer:Timer;
		
		public function get description():String
		{
			return m_description;
		}

		public function Slide( url:String, description:String ) 
		{
			m_url = url;
			m_description = description;
			m_loaded = false;
			this.alpha = 0;
			this.addEventListener( Event.REMOVED_FROM_STAGE, startUnloadTimer );
			this.addEventListener( Event.ADDED_TO_STAGE, stopUnloadTimer );
		}
		
		private function startUnloadTimer( event:Event ):void
		{
			trace( "Starting unload timer for " + m_url );
			if ( m_unloadTimer != null )
			{
				m_unloadTimer.reset();
				m_unloadTimer.start();
			}
			else
			{
				// Create a new timer - we'll dispose this 30 seconds after it has been removed.
				m_unloadTimer = new Timer( 30000, 1 );
				m_unloadTimer.addEventListener( TimerEvent.TIMER, unload );
				m_unloadTimer.start();
			}
		}
		
		private function stopUnloadTimer( event:Event ):void
		{
			if ( m_unloadTimer != null )
			{
				// We've been added back in so reset the timer and don't re-start it.
				m_unloadTimer.reset();
			}
		}

		public function fadeIn( seconds:Number ):void
		{
			this.alpha = 0;
			var tw:GTweeny = new GTweeny(this, seconds, { alpha: 1 } );
			tw.addEventListener( Event.COMPLETE, fadeInComplete );
			tw.play();
		}
		private function fadeInComplete( event:Event ):void
		{
			this.dispatchEvent( new Event( Slide.FADE_IN_COMPLETE ) );
		}
		public function fadeOut( seconds:Number ):void
		{
			this.alpha = 1;
			var tw:GTweeny = new GTweeny(this, seconds, { alpha: 0 } );
			tw.addEventListener( Event.COMPLETE, fadeOutComplete );
			tw.play();
		}
		
		private function fadeOutComplete( event:Event ):void
		{
			this.dispatchEvent( new Event( Slide.FADE_OUT_COMPLETE ) );
		}

		private var m_loading:Boolean;
		
		public function load():void
		{
			if ( m_loading )
			{
				trace( "Load called when already loading, ignoring..." );
				return;
			}
			m_loading = true;
			if ( m_loaded )
			{
				trace( "Already loaded, resizing and firing event." );
				// Already loaded, so simply resize if neccessary, fire event, 
				// and we're good.
				resize();
				m_loading = false;
				this.dispatchEvent( new Event( Slide.LOADED ) );
				return;
			}
			var ldr:Loader = new Loader();
			ldr.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress);
			ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
			ldr.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadFailed);
			ldr.load( new URLRequest(m_url) );
			ldr.visible = true;
		}
		
		private function unload( event:TimerEvent = null ):void
		{
			if ( !m_loaded ) 
			{
				// Not loaded, so nothing to do.
				trace( "Nothing to unload for " + m_url );
				return;
			}
			trace( "Unloading slide " + m_url );
			m_loaded = false;
			this.removeChild( m_bmp );
			m_bmp.bitmapData.dispose();
			m_bmp = null;
			if ( m_caption != null )
			{
				if ( this.contains( m_caption ) ) this.removeChild( m_caption );
				m_caption = null;
			}
			m_percentLoaded = 0;
		}

		private function loadProgress(event:ProgressEvent):void
		{
			m_percentLoaded = Math.round( (event.bytesLoaded/event.bytesTotal) * 100 ); 
		}

		private function loadComplete(event:Event):void
		{
			trace("Loading complete");
			m_loaded = true;
			m_percentLoaded = 100;
			var scale:Number = 1.0;
			m_bmp = event.target.content as Bitmap;
			m_bmp.smoothing = true;
			m_bmp.pixelSnapping = PixelSnapping.NEVER;
			this.addChild( m_bmp );
			resize();
			m_loading = false;
			this.dispatchEvent( new Event( Slide.LOADED ) );
		}
		
		private function loadFailed(event:IOErrorEvent):void
		{
			m_loading = false;
			this.dispatchEvent( new Event( Slide.FAILED_LOAD ) );
		}
				
		public function resize():void 
		{
			if ( m_bmp == null ) return;
			if ( !stage )
			{
				trace( "Not on stage, skipping re-size." );
				return;
			}
			var scale:Number = 1.0;
			if ( m_bmp.width > m_bmp.height ) 
			{
				scale = stage.stageWidth / (m_bmp.width / m_bmp.scaleX);
				// Check the height now fits.
				if ( ((m_bmp.height / m_bmp.scaleY) * scale) > stage.stageHeight )
				{
					// Nope, so use the height to scale.
					trace( "Height doesn't fit when scaling by width, use height to scale instead" );
					scale = stage.stageHeight / (m_bmp.height / m_bmp.scaleY );
				}
			}
			else 
			{
				scale = stage.stageHeight / (m_bmp.height / m_bmp.scaleY );
				// Check the width now fits.
				if ( ((m_bmp.width / m_bmp.scaleX) * scale) > stage.stageWidth )
				{
					trace( "Width doesn't fit when scaling by height, use width to scale instead" );
					// Nope, so use the width to scale.
					scale = stage.stageWidth / (m_bmp.width / m_bmp.scaleX);
				}
			}

			trace( "Re-scale: " + scale );
			m_bmp.scaleX = scale;
			m_bmp.scaleY = scale;
			// Re-center
			trace( "Size - width: " + m_bmp.width + ", height: " + m_bmp.height );
			trace( "Stage size - width: " + stage.width + ", height: " + stage.height );
			trace( "Stage size - stageWidth: " + stage.stageWidth + ", stageHeight: " + stage.stageHeight );
			if ( stage.stageWidth - m_bmp.width > 0 )
			{
				m_bmp.x = (stage.stageWidth - m_bmp.width) / 2;
			}
			else
			{
				m_bmp.x = 0;
			}
			if ( stage.stageHeight - m_bmp.height > 0 )
			{
				m_bmp.y = (stage.stageHeight - m_bmp.height) / 2;
			}
			else
			{
				m_bmp.y = 0;
			}
			trace( "Pos - x: " + m_bmp.x + ", y: " + m_bmp.y );
			
			if ( m_caption != null )
			{
				m_caption.x = (stage.stageWidth - m_caption.width) / 2;
				m_caption.y = (stage.stageHeight - 100);
			}
		}
		
		public function showCaption( num:int, of:int ):void
		{
			trace( "showCaption");
			if ( m_caption == null )
			{
				m_caption = new TextField();
				var captionFormat:TextFormat = new TextFormat( "_Font", 24, 0xFFFFFFFF, true );
				captionFormat.leftMargin = 30;
				captionFormat.rightMargin = 30;
				m_caption.defaultTextFormat = captionFormat;
				m_caption.antiAliasType = AntiAliasType.ADVANCED;
				m_caption.embedFonts = true;
				m_caption.alpha = 0.75;
				m_caption.background = true;
				//m_caption.opaqueBackground = false;
				m_caption.backgroundColor = 0xFF222222;
				m_caption.border = true;
				m_caption.borderColor = 0xFFFFFFFF;				
				
				if ( m_description != null && m_description.length > 0 )
				{
					m_caption.text = num + "/" + of + ": " + m_description;
				}
				else
				{
					m_caption.text = num + "/" + of;
				}
				m_caption.autoSize = TextFieldAutoSize.CENTER;
				m_caption.x = (stage.stageWidth - m_caption.width) / 2;
				m_caption.y = (stage.stageHeight - 100);
				this.addChild( m_caption );
			}
			else
			{
				if ( !this.contains( m_caption ) )
				{
					m_caption.x = (stage.stageWidth - m_caption.width) / 2;
					m_caption.y = (stage.stageHeight - 100);
					this.addChild( m_caption );
				}
			}
		}
		public function hideCaption():void
		{
			trace( "hideCaption" );
			if ( m_caption != null && this.contains( m_caption ) )
			{
				this.removeChild( m_caption );
			}
		}
	}
}